package com.molichuxing.gateway.utils;

import cn.hutool.core.codec.Base64;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.molichuxing.framework.result.ResponseResult;
import com.molichuxing.framework.utils.StringUtil;
import com.molichuxing.gateway.bff.permission.entity.response.DesenseVo;
import com.molichuxing.gateway.property.DesensePosEnum;
import com.molichuxing.services.infrastructure.dto.response.response.SensitiveWordDto;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class DesenseUtil {

    private final static Logger logger = LoggerFactory.getLogger(DesenseUtil.class);

    private static final List<String> DESENSITIZE_DATA = Arrays.asList("gmtCreate", "gmtModified", "payDate", "repaymentDate", "orderTimes");

    /**
     * 只显示第一个汉字，其他隐藏为2个星号<例子：李**>
     *
     * @param fullName
     * @param index    1 为第index位
     * @return
     */
    public static String left(String fullName, int index, String desenseFlag) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        String name = StringUtils.left(fullName, index);
        return StringUtils.rightPad(name, StringUtils.length(fullName), desenseFlag);
    }

    /**
     * [固定电话] 后四位，其他隐藏<例子：****1234>
     *
     * @param num
     * @return
     */
    public static String right(String num, int end, String desenseFlag) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.leftPad(StringUtils.right(num, end), StringUtils.length(num), desenseFlag);
    }

    /**
     * [固定电话] 后四位，其他隐藏<例子：****1234>
     *
     * @param num
     * @return
     */
    public static String all(String num, String desenseFlag) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.leftPad(StringUtils.right(num, 0), StringUtils.length(num), desenseFlag);
    }


    /**
     * 固定合适：前几后几
     *
     * @param frontNum
     * @return
     */
    public static String around(String fullValue, Integer frontNum, Integer backNum, String desenseFlag) {
        if (StringUtils.isBlank(fullValue)) {
            return "";
        }
        if (frontNum == null || backNum == null) {
            return fullValue;
        }
        if (frontNum == null && backNum != null) {
            return frontFew(fullValue, frontNum, desenseFlag);
        }
        if (frontNum != null && backNum == null) {
            return backFew(fullValue, frontNum, desenseFlag);
        }
        if ((fullValue.length() < frontNum) || (fullValue.length() < backNum)
                || (fullValue.length() < backNum + frontNum)) {
            return all(fullValue, desenseFlag);
        }
        return StringUtils.left(fullValue, frontNum).concat(generateDesensitizeChar(fullValue.length() - frontNum - backNum, desenseFlag)).concat(StringUtils.right(fullValue, backNum));
    }

    /**
     * 固定合适：前几
     *
     * @param frontNum
     * @return
     */
    public static String frontFew(String fullValue, Integer frontNum, String desenseFlag) {
        if (StringUtils.isBlank(fullValue)) {
            return "";
        }
        if (frontNum == null) {
            return fullValue;
        }
        if (frontNum >= fullValue.length()) {
            return fullValue;
        }
        return StringUtils.rightPad(StringUtils.left(fullValue, frontNum), StringUtils.length(fullValue), desenseFlag);
    }

    /**
     * 固定合适：后几
     *
     * @param backNum
     * @return
     */
    public static String backFew(String fullValue, Integer backNum, String desenseFlag) {
        if (StringUtils.isBlank(fullValue)) {
            return "";
        }
        if (backNum == null) {
            return fullValue;
        }
        if (backNum >= fullValue.length()) {
            return fullValue;
        }
        return StringUtils.leftPad(StringUtils.right(fullValue, (backNum)), StringUtils.length(fullValue), desenseFlag);
    }

    private static String generateDesensitizeChar(int num, String desenseFlag) {
        if (num == 0) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i <= num; i++) {
            sb.append(desenseFlag);
        }
        return sb.toString();
    }

    /**
     * 脱敏集合。
     *
     * @param data
     * @param desenseMap
     * @return
     */
    public static Map<Object, Object> convertMap(Map<Object, Object> data, Map<String, String> desenseMap, Map<String, String> sensitiveWords, Object returnValue) throws Exception {
        Map<Object, Object> result = new HashMap<>();
        Set<Object> it = data.keySet();
        for (Object key : it) {
            if (data.get(key + "") instanceof List) {
                Object classObj = getFieldValueByObject(((ResponseResult) returnValue).getData(), key + "");
                if (classObj != null && (((List) classObj)).size() > 0) {
                    Class<?> classes = ((List) classObj).get(0).getClass();
                    result.put(key, convertList((List) data.get(key + ""), desenseMap, sensitiveWords, classes));
                } else {
                    result.put(key, new ArrayList<>());
                }
            } else if (data.get(key + "") instanceof Long) {
                try {
                    if (DESENSITIZE_DATA.contains(key) || key.toString().endsWith("Time") || key.toString().endsWith("Date")) {
                        result.put(key, new Date(Long.parseLong(String.valueOf(data.get(key + "")))));
                    } else {
                        result.put(key, data.get(key + ""));
                    }
                } catch (Exception e) {
                    logger.info("转换时间错误。e:" + e);
                    result.put(key, data.get(key + ""));
                }
            } else if (data.get(key + "") instanceof Integer) {
                result.put(key, data.get(key + ""));
            } else if (data.get(key + "") instanceof BigDecimal) {
                result.put(key, data.get(key + ""));
            } else {
                Class<?> classes = getFieldValueByObject(((ResponseResult) returnValue).getData(), key + "").getClass();
                result.put(key, convertList(data.get(key + ""), desenseMap, sensitiveWords, classes));
            }
        }
        return result;
    }

    /**
     * 脱敏集合。
     *
     * @param data
     * @param desenseMap
     * @return
     */
    public static List<Object> convertList(List<Object> data, Map<String, String> desenseMap, Map<String, String> sensitiveWords, Class<?> classes) throws Exception {
        List<Object> dataList = new ArrayList<>();
        for (Object param : data) {
            dataList.add(convertList(param, desenseMap, sensitiveWords, classes));
        }
        return dataList;
    }

    /**
     * 脱敏单一对象
     *
     * @param param
     * @return
     */
    public static Object convertList(Object param, Map<String, String> desenseMap, Map<String, String> sensitiveWords, Class<?> classes) throws Exception {
//        if (!(param instanceof JSONObject)) {
//            return param;
//        }


        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance);
        module.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        module.addSerializer(LocalTime.class,
                new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        module.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        // boolean转换0/1
        BooleanSerializer serialize = new BooleanSerializer();
        module.addSerializer(Boolean.class, serialize);
        module.addSerializer(boolean.class, serialize);
        ObjectMapper mapper = new ObjectMapper();

        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.registerModules(module);

//        Object classawqa = mapper.readValue(mapper.writeValueAsString(param), classes);

        String voName = classes.getSimpleName();

        Map<String, Object> dataJson = mapper.readValue(mapper.writeValueAsString(param), Map.class);
        Set<String> it = dataJson.keySet();
        for (String key : it) {
            if (null != dataJson.get(key)) {
                if (dataJson.get(key) instanceof List) {
                    List list = (List) dataJson.get(key);
                    Class className = list.get(0).getClass();
                    if (list.size() > 0) {
                        if (!className.equals(java.lang.Integer.class) &&
                                !className.equals(java.lang.Byte.class) &&
                                !className.equals(java.lang.Long.class) &&
                                !className.equals(java.lang.Double.class) &&
                                !className.equals(java.lang.Float.class) &&
                                !className.equals(java.lang.Character.class) &&
                                !className.equals(java.lang.Short.class) &&
                                !className.equals(java.lang.Boolean.class)) {
                            list = convertList(list, desenseMap, sensitiveWords, list.get(0).getClass());
                            dataJson.put(key, list);
                        }
                    }
                } else {
                    operateDesenseData(key, String.valueOf(dataJson.get(key)), desenseMap, dataJson, voName);
                    operateSensitiveWordData(key, sensitiveWords, dataJson);
                }
            }
        }
        return dataJson;
    }

    private static void operateSensitiveWordData(String key, Map<String, String> sensitiveWords, Map<String, Object> dataJson) {
        String value = String.valueOf(dataJson.get(key));
        Set<String> keySet = sensitiveWords.keySet();
        for (String words : keySet) {
            if (!StringUtil.isEmpty(words)) {
                String decodeWords = Base64.decodeStr(words);
                if (!StringUtil.isEmpty(decodeWords) && value.contains(decodeWords)) {
                    SensitiveWordDto dto = JSONObject.parseObject(sensitiveWords.get(words), SensitiveWordDto.class);
                    if (dto != null) {
                        dataJson.put(key, replaceStr(value, decodeWords, dto.getWordsFlag()));
                    }
                }
            }
        }
    }

    private static String replaceStr(String value, String words, String flag) {
        if (value.contains(words)) {
            String flagStr = "";
            for (int l = 0; l < words.length(); l++) {
                flagStr += flag;
            }
            value = value.replace(words, flagStr);
            replaceStr(value, words, flag);
        }
        return value;
    }

    private static void operateDesenseData(String key, String value, Map<String, String> desenseMap, Map<String, Object> dataJson, String voName) {
        if (!StringUtil.isEmpty(voName) && desenseMap.get(voName + "-" + key) != null) {
            DesenseVo desense = JSONObject.parseObject(desenseMap.get(voName + "-" + key), DesenseVo.class);
            if (desense != null) {
                Integer desensePos = desense.getDesensePos(); //脱敏位置：(1:左，2:中，3:右)
                if (desensePos == DesensePosEnum.BEFORE.getValue()) { //之前
                    dataJson.put(key, DesenseUtil.backFew(value, desense.getDesenseCharNum(), desense.getDesenseFlag()));
                } else if (desensePos == DesensePosEnum.AROUND.getValue()) { //之后

                    dataJson.put(key, DesenseUtil.around(value, desense.getDesenseCharNum(), desense.getDesenseCharNum(), desense.getDesenseFlag()));
                } else if (desensePos == DesensePosEnum.AFTER.getValue()) {
                    dataJson.put(key, DesenseUtil.frontFew(value, desense.getDesenseCharNum(), desense.getDesenseFlag()));
                }
            }
        }
    }

    public static Object getFieldValueByObject(Object object, String targetFieldName) throws Exception {

        // 获取该对象的Class
        Class objClass = object.getClass();
        // 获取所有的属性数组
        Field[] fields = objClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 属性名称
            String currentFieldName = "";
            // 获取属性上面的注解 import com.fasterxml.jackson.annotation.JsonProperty;
            boolean has_JsonProperty = field.isAnnotationPresent(JsonProperty.class);
            if (has_JsonProperty) {
                currentFieldName = field.getAnnotation(JsonProperty.class).value();
            } else {
                currentFieldName = field.getName();
            }
            if (currentFieldName.equals(targetFieldName)) {
                field.get(object);
                return field.get(object); // 通过反射拿到该属性在此对象中的值(也可能是个对象)
            }
        }
        return null;
    }

    public static Map<String, Object> Obj2Map(Object obj) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        List<Field> fields = new ArrayList<>();
        Class tempClass = obj.getClass();
        while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
        }
        for (Field field : fields) {
            if (!"serialVersionUID".equals(field.getName())) {
                field.setAccessible(true);

                map.put(field.getName(), field.get(obj));
            }
        }
        return map;
    }

    static class BooleanSerializer extends JsonSerializer<Boolean> {
        @Override
        public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
            if (value != null && value) {
                gen.writeNumber(1);
            } else {
                gen.writeNumber(0);
            }
        }
    }
}
